001    /*
002     * CSHApplication.java
003     *
004     * Created on July 10, 2003, 11:22 AM
005     *
006     * This file is part of the STAR Scheduler.
007     * Copyright (c) 2002-2003 STAR Collaboration - Brookhaven National Laboratory
008     *
009     * STAR Scheduler is free software; you can redistribute it and/or modify
010     * it under the terms of the GNU General Public License as published by
011     * the Free Software Foundation; either version 2 of the License, or
012     * (at your option) any later version.
013     *
014     * STAR Scheduler is distributed in the hope that it will be useful,
015     * but WITHOUT ANY WARRANTY; without even the implied warranty of
016     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
017     * GNU General Public License for more details.
018     *
019     * You should have received a copy of the GNU General Public License
020     * along with STAR Scheduler; if not, write to the Free Software
021     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
022     */
023    package gov.bnl.star.offline.scheduler.lsf;
024    
025    import gov.bnl.star.offline.scheduler.*;
026    import gov.bnl.star.offline.scheduler.catalog.PhysicalFile;
027    import gov.bnl.star.offline.scheduler.util.FileListToolkit;
028    import gov.bnl.star.offline.scheduler.util.FilesystemToolkit;
029    import gov.bnl.star.offline.scheduler.util.VariablesToolkit;
030    
031    import java.io.File;
032    import java.io.FileOutputStream;
033    import java.io.PrintStream;
034    
035    import java.util.*;
036    import java.util.logging.Level;
037    import java.util.logging.Logger;
038    
039    
040    /** Application module for csh.
041     * <p>
042     * This module defines the interface of a csh task, and prepare the task
043     * information in the following environment variables:
044     * <p>
045     * $JOBID - the unique job id given by the scheduler<br>
046     * $FILESLIST - the file name of a text file containing the file list assigned to
047     * the job<br>
048     * $INPUTFILECOUNT - the number of files assigned to the job<br>
049     * $INPUTFILExx - the input files associated to the job<br>
050     * <p>
051     * @author Gabriele Carcassi, Jerome Lauret
052     * @version 1.0 2002/12/26
053     */
054    public class CSHApplication {
055        static private Logger log = Logger.getLogger(CSHApplication.class.getName());
056        private Request request;
057        private Job job;
058        private String scratchDir;
059        private String submissionCommand;
060        private Map variables;
061    
062        /** Holds value of property recurseCopy. */
063        private boolean recurseCopy = false;
064        
065        /** Holds the value for the property of EnvVariableLimit */
066        private int EnvVariableLimit;
067        
068        /** Holds value of property copyScript. */
069        private String copyScript;
070        
071        /** Holds value of property storageScript. */
072        private String storageScript;
073        
074        /** Holds value of property registerScript. */
075        private String registerScript;
076        
077        /** Creates a new instance of CSHApplication */
078        public CSHApplication() {
079        }
080    
081        /** Returns the CSH application module. In the future, the application should
082         * be taken directly from the component library. This method is provided
083         * for compatibility with the old framework.
084         * @return the CSH application module
085         */
086        public static CSHApplication getInstance() {
087            return (CSHApplication) ComponentLibrary.getInstance().getComponent("CSHApplication");
088        }
089    
090        /** Prepares the task, by creating the script. */
091        public void prepareJob() {
092            try {
093                PrintStream inputFileList = new PrintStream(new FileOutputStream(
094                            new File(getInputFileListName())));
095                createInputFileList(inputFileList);
096            } catch (Exception e) {
097                log.log(Level.SEVERE, "Couldn't create the file list", e);
098                throw new RuntimeException("Couldn't create the file list " +
099                    getInputFileListName() + ": " + e.getMessage());
100            }
101            
102            try {
103                PrintStream cshScript = new PrintStream(new FileOutputStream(
104                            new File(getCSHScriptName())));
105                createCSHScript(cshScript);
106                makeScriptFileExecutable(getCSHScriptName());
107            } catch (Exception e) {
108                log.log(Level.SEVERE, "Couldn't create the script", e);
109                throw new RuntimeException("Couldn't create the script " +
110                    getCSHScriptName() + ": " + e.getMessage());
111            }
112        }
113    
114        /** Returns the command line to execute the task set by <CODE>setJob</CODE>.
115         * @return Command line to execute the task
116         */
117        public String getCommandLine() {
118            return getCSHScriptName();
119        }
120    
121        /* Calls chmod to make the script executable. */
122        private void makeScriptFileExecutable(String fileName) {
123            // Hack to make the Unit tests work on windows.
124            // chmod doesn't exist on Windows (if you don't have cygwin)
125            if (System.getProperty("os.name").startsWith("Win")) return;
126            
127            try {
128                String chmod = "chmod +x " + fileName;
129                log.info("Executing \"" + chmod + "\"");
130    
131                java.lang.Process shell = Runtime.getRuntime().exec(chmod);
132                shell.waitFor();
133            } catch (Exception e) {
134                log.log(Level.SEVERE, "Couldn't make the script executable", e);
135                throw new RuntimeException("Couldn't make the script " + fileName +
136                    " executable", e);
137            }
138        }
139    
140        /** Sets the task the application will have to prepare.
141         * @param request the request that originated the task
142         * @param job the job to be executed
143         */
144        public void setJob(Request request, Job job) {
145            this.request = request;
146            this.job = job;
147            scratchDir = null;
148            submissionCommand = null;
149            variables = null;
150        }
151    
152        /** Sets the scratch directory for the temporary output files of the job.
153         * @param scratchDir the temporary output directory
154         */
155        public void setScratchDir(String scratchDir) {
156            this.scratchDir = scratchDir;
157        }
158    
159        /** Sets the submittion command used to submit the job. This will be included in the
160         * script for resubmission.
161         * @param submissionCommand the command line to submit the job to the batch system
162         */
163        public void setSubmissionCommand(String submissionCommand) {
164            this.submissionCommand = submissionCommand;
165        }
166    
167        /** Generates the csh script to be submitted through LSF.
168         * @param out the stream on which to print the script
169         */
170        private void createCSHScript(PrintStream out) {
171            // Sets the shell that will interpret the script
172            
173            ////////////test area////////////////
174            Map config = (Map) ComponentLibrary.getInstance().getComponent("registerScript");
175            
176            out.println("#!/bin/csh");
177    
178            out.println("# ------------------- ");
179            out.println("# Script generated  at " + new Date() +
180                " by the STAR scheduler and submitted with");
181            out.println("# " + submissionCommand);
182            out.println("# ------------------- ");
183    
184            Map env = getEnvironmentVariables();
185            
186            Iterator variables = env.keySet().iterator();
187            Iterator FilesNameVariable = env.keySet().iterator();
188    
189            // Sets all the environment variable.
190            out.println();
191            out.println("# Preparing environment variables");
192    
193           String varName, varValue;
194            
195            
196            int    counter=0;
197            String padding="";
198            while (variables.hasNext()) { //First pass prints all the environment variables that do not start with “INPUTFILE” 
199                varName  = (String) variables.next();
200                varValue = (String) env.get(varName);
201                if((! varName.startsWith("INPUTFILE")) || (varName.matches("INPUTFILECOUNT"))){     
202                    out.println(padding + "setenv " + varName + " " + varValue);
203                }
204            }
205            
206            while (FilesNameVariable.hasNext()) { //Second pass prints all the environment variables that start with “INPUTFILE”  
207                varName  = (String) FilesNameVariable.next();
208                varValue = (String) env.get(varName);
209                if(varName.startsWith("INPUTFILE") && (! varName.matches("INPUTFILECOUNT"))){   
210                    if (counter == EnvVariableLimit){ 
211                        out.println("#");
212                        out.println("# Note: The configuration requested a limitation of the");
213                        out.println("# number of INPUTFILE(n) environemnt variables at "+EnvVariableLimit);
214                        out.println("# The rest of the variables will be commented.");
215                        out.println("#");
216                        padding = "# ";
217                    } 
218                    counter++;
219                    out.println(padding + "setenv " + varName + " " + varValue);
220                }
221            }
222    
223            // Creates the scratch directory
224            out.println();
225            out.println("# Creating the scratch directory, return failure status");
226            out.println("mkdir -p $SCRATCH");
227            out.println("set STS=$status");
228            out.println("if (! -d $SCRATCH) then");
229            out.println("    echo \"Scheduler:: Failed to create $SCRATCH on $HOST\"");
230            out.println("    exit $STS");
231            out.println("endif");
232    
233            // Adds the command to be executed
234            out.println();
235            out.println("###################################################");
236            //out.println("/bin/csh -s ___EOD"+job.getJobID()+"__");
237            out.println("# User command BEGIN ----------------------------->");
238            
239            out.println(request.getCommand());
240    
241            out.println("# <------------------------------User command BEGIN");
242            //out.println("___EOD"+job.getJobID()+"__");
243            out.println("###################################################");
244    
245            // Copy output files
246            out.println();
247            out.println("# Copy output files (if any where specified)");
248    
249            out.print(generateScriptFragment(request.getOutputList()));
250    
251            // Delete the scratch directory
252            out.println();
253            out.println("# Delete the scratch directory");
254            out.println("/bin/rm -fr $SCRATCH");
255        }
256        
257        String generateScriptFragment(List outputList) {
258            StringBuffer buffer = new StringBuffer();
259            for (int nOutput = 0; nOutput < outputList.size(); nOutput++) {
260                OutputFile output = (OutputFile) outputList.get(nOutput);
261                if (output.getActionList().size() != 0) {
262                    buffer.append("setenv OUTPUTFILE").append(nOutput).append(" file:/$SCRATCH/").
263                    append(output.getFromScratch()).append("\n");
264                    for (int nOutputAction = 0; nOutputAction < output.getActionList().size(); nOutputAction++) {
265                        if (output.getActionList().get(nOutputAction) instanceof OutputCopyAction) {
266                            buffer.append(generateScriptFragment(nOutput, (OutputCopyAction) output.getActionList().get(nOutputAction)));
267                        } else if (output.getActionList().get(nOutputAction) instanceof OutputRegisterAction) {
268                            buffer.append(generateScriptFragment(nOutput, (OutputRegisterAction) output.getActionList().get(nOutputAction)));
269                        } else {
270                            log.severe("Unsupported OutputAction time found: " + output.getActionList().get(nOutputAction).getClass().getName());
271                            throw new RuntimeException("Unsupported OutputAction time found");
272                        }
273                    }
274                } else {
275                    if (recurseCopy) {
276                        buffer.append("/bin/cp -r $SCRATCH/").append(output.getFromScratch()).append(" ").
277                        append(output.getToURL().getPath()).append("\n");
278                    } else {
279                        buffer.append("/bin/cp $SCRATCH/").append(output.getFromScratch()).append(" ").
280                        append(output.getToURL().getPath()).append("\n");
281                    }
282                }
283            }
284            return buffer.toString();
285        }
286        
287        String generateScriptFragment(int nOutput, OutputCopyAction action) {
288            StringBuffer fragment = new StringBuffer();
289            if (action.getFileRef() != null) {
290                if (action.getStorageService() != null) {
291                    fragment.append("setenv OUTPUTFILE").append(nOutput).append('_').
292                    append(action.getFileRef()).append(" `").append(getStorageScript()).
293                    append(" \"").append(action.getStorageService()).append("\" `").
294                    append(action.getURI().getPath()).append("\n");
295                    
296                } else {
297                    fragment.append("setenv OUTPUTFILE").append(nOutput).append('_').
298                    append(action.getFileRef()).append(" ").
299                    append(action.getURI()).append("\n");
300                }
301                fragment.append(getCopyScript()).append(" $OUTPUTFILE").append(nOutput).
302                append(" $OUTPUTFILE").append(nOutput).append('_').append(action.getFileRef()).
303                append("\n");
304            } else {
305                fragment.append(getCopyScript()).append(" $OUTPUTFILE").append(nOutput);
306                if (action.getStorageService() != null) {
307                    fragment.append(" `").append(getStorageScript()).
308                    append(" \"").append(action.getStorageService()).append("\" `").
309                    append(action.getURI().getPath()).append("\n");
310                } else {
311                    fragment.append(" ").append(action.getURI()).append("\n");
312                }
313            }
314            return fragment.toString();
315        }
316    
317        String generateScriptFragment(int nOutput, OutputRegisterAction action) {
318            StringBuffer fragment = new StringBuffer();
319            fragment.append(getRegisterScript()).append(" $OUTPUTFILE").append(nOutput);
320            if (action.getFileRef() != null) {
321                fragment.append('_'). append(action.getFileRef());
322            }
323            int queryStart = action.getURI().getSchemeSpecificPart().indexOf('?');
324            queryStart++;
325            fragment.append(" '").
326            append(action.getURI().getSchemeSpecificPart().substring(queryStart)).append("'\n");
327            return fragment.toString();
328        }
329    
330        /** Generates the input file list. It consists of full path names, one line for each
331         * one.
332         * @param out the stream on which to print the file list
333         */
334        private void createInputFileList(PrintStream out) {
335            if ("paths".equals(request.getFileListType())) {
336                FileListToolkit.createPathsFileList(out, job.getInput());
337            } else if ("rootd".equals(request.getFileListType())) {
338                FileListToolkit.createRootdFileList(out, job.getInput());
339            }
340        }
341    
342        /** Returns the name for the csh script.
343         * @return the csh script name
344         */
345        public String getCSHScriptName() {
346            return FilesystemToolkit.getCurrentDirectory() + "/sched" +
347            job.getJobID() + ".csh";
348        }
349    
350        /** Returns the name for the input file list file.
351         * @return the input file list name
352         */
353        public String getInputFileListName() {
354            return "sched" + job.getJobID() + ".list";
355        }
356        
357        
358        private Map getEnvironmentVariables() {
359            if (variables != null) {
360                return variables;
361            }
362    
363            variables = new Hashtable();
364    
365            variables = new TreeMap(job.getEnv());
366            variables.put("JOBID", job.getJobID());
367            variables.put("REQUESTID", job.getRequestID());
368            variables.put("PROCESSID", job.getProcessID());
369    
370            // FIXME: scratch dir should be passed in a nicer way
371            variables.put("SCRATCH", scratchDir);
372            variables.put("FILELIST", getInputFileListName());
373            
374            List inputList = job.getInput();
375            variables.put("INPUTFILECOUNT", Integer.toString(inputList.size()));
376    
377            for (int nFile = 0; nFile < inputList.size(); nFile++) {
378                PhysicalFile file = (PhysicalFile) inputList.get(nFile);
379                variables.put("INPUTFILE" + nFile,
380                    file.getPath() + "/" + file.getFilename());
381            }
382            
383           
384            //If there is only one input file per csh make an Environment Variable called FILEBASENAME
385            if(inputList.size() == 1){
386                PhysicalFile file = (PhysicalFile) inputList.get(0);
387                if(! (file.getFilename().indexOf(".") == -1))
388                    variables.put("FILEBASENAME",file.getFilename().substring(0,file.getFilename().indexOf(".")));
389                else
390                    variables.put("FILEBASENAME",file.getFilename());   
391            }
392            else{ //All other times FILEBASENAME will be set to this value
393                variables.put("FILEBASENAME","FILEBASENAME.NOT.INITIALIZED." + job.getJobID());
394            }
395    
396            return variables;
397        }
398    
399        /** Returns the file for standard input redirection.
400         * @return stdin file
401         */
402        public String getStdin() {
403            if (job.getStdin() != null) {
404                return VariablesToolkit.substituteVariables(job.getStdin().getPath(),
405                    CSHApplication.getInstance().getEnvironmentVariables());
406            }
407    
408            return null;
409        }
410    
411        /** Returns the file for standard output redirection.
412         * @return stdout file
413         */
414        public String getStdout() {
415            if (job.getStdout() != null) {
416                if (job.getStdout() == Request.discard) {
417                    return "/dev/null";
418                }
419    
420                return VariablesToolkit.substituteVariables(job.getStdout().getPath(),
421                    CSHApplication.getInstance().getEnvironmentVariables());
422            }
423    
424            return null;
425        }
426    
427        /** Returns the file for standard error redirection.
428         * @return stderr file
429         */
430        public String getStderr() {
431            if (job.getStderr() != null) {
432                if (job.getStderr() == Request.discard) {
433                    return "/dev/null";
434                }
435    
436                return VariablesToolkit.substituteVariables(job.getStderr().getPath(),
437                    CSHApplication.getInstance().getEnvironmentVariables());
438            }
439    
440            return null;
441        }
442    
443        /** Returns the job name to be assigned to the batch system.
444         * @return the job name to be used by the batch system
445         */
446        public String getJobName() {
447            return request.getName();
448        }
449        
450        /** Getter for property recurseCopy.
451         * @return Value of property recurseCopy.
452         *
453         */
454        public boolean isRecurseCopy() {
455            return this.recurseCopy;
456        }
457        
458        /** Setter for property recurseCopy.
459         * @param recurseCopy New value of property recurseCopy.
460         *
461         */
462        public void setRecurseCopy(boolean recurseCopy) {
463            this.recurseCopy = recurseCopy;
464        }
465        
466        
467        /** Getter for property EnvVariableLimit.
468         * @return Value of property EnvVariableLimit.
469         *
470         */
471        public int getEnvVariableLimit() {
472            return this.EnvVariableLimit;
473        }
474        
475        /** Setter for property EnvVariableLimit.
476         * @param EnvVariableLimit New value of property EnvVariableLimit.
477         *
478         */
479        public void setEnvVariableLimit(int EnvVariableLimit) {
480            this.EnvVariableLimit = EnvVariableLimit;
481        }
482        
483        
484        /** Getter for property copyScript.
485         * @return Value of property copyScript.
486         *
487         */
488        public String getCopyScript() {
489            return this.copyScript;
490        }
491        
492        /** Setter for property copyScript.
493         * @param copyScript New value of property copyScript.
494         *
495         */
496        public void setCopyScript(String copyScript) {
497            this.copyScript = copyScript;
498        }
499        
500        /** Getter for property storageScript.
501         * @return Value of property storageScript.
502         *
503         */
504        public String getStorageScript() {
505            return this.storageScript;
506        }
507        
508        /** Setter for property storageScript.
509         * @param storageScript New value of property storageScript.
510         *
511         */
512        public void setStorageScript(String storageScript) {
513            this.storageScript = storageScript;
514        }
515        
516        /** Getter for property registerScript.
517         * @return Value of property registerScript.
518         *
519         */
520        public String getRegisterScript() {
521            return this.registerScript;
522        }
523        
524        /** Setter for property registerScript.
525         * @param registerScript New value of property registerScript.
526         *
527         */
528        public void setRegisterScript(String registerScript) {
529            this.registerScript = registerScript;
530        }
531        
532    }